import SEO from "../components/SEO";

<SEO title="Tooltip" description="Accessible tooltips component for React" />

# Tooltip

- Source: https://github.com/reach/reach-ui/tree/main/packages/tooltip
- WAI-ARIA: https://www.w3.org/TR/wai-aria-practices-1.2/#tooltip

When the user's mouse or focus rests on an element, a non-interactive popup is displayed near it.

A couple notes on using tooltips:

- Do not use tooltips for information vital to task completion. The elements they are attached to should usually make sense on their own, but a tooltip can help provide a little bit more information--especially for new users of your app.
- Keep the content minimal, they are not intended to hide critical content.
- If you want interactive content, you can use a [Dialog](/dialog).

_Touch Events_: Touch events are currently not supported. There's not a lot of research or examples of these types of tooltips on mobile. We have some ideas but need to validate them first before implementing. Please adjust your interfaces on mobile to account for this.

```jsx
// jsx-demo
function Example() {
	return (
		<div>
			<Tooltip label="Notifications">
				<button style={{ fontSize: 25 }}>
					<span>🔔</span>
				</button>
			</Tooltip>
			<Tooltip label="Settings">
				<button style={{ fontSize: 25 }}>
					<span aria-hidden>⚙️</span>
				</button>
			</Tooltip>

			<div style={{ float: "right" }}>
				<Tooltip label="Notifications" aria-label="3 Notifications">
					<button style={{ fontSize: 25 }}>
						<span>🔔</span>
						<span>3</span>
					</button>
				</Tooltip>
			</div>
		</div>
	);
}
```

## Installation

From the command line in your project directory, run `npm install @reach/tooltip` or `yarn add @reach/tooltip`. Then import the components and styles that you need:

```bash
npm install @reach/tooltip
# or
yarn add @reach/tooltip
```

```js
import { Tooltip, TooltipPopup, useTooltip } from "@reach/tooltip";
import "@reach/tooltip/styles.css";
```

## Usage

```jsx
// jsx-demo
function Example() {
	return (
		<Tooltip label="Save">
			<button style={{ fontSize: 25 }}>💾</button>
		</Tooltip>
	);
}
```

Sometimes the button contains information that is relevant to the user, like a badge showing the count for notifications. In these cases we want the screen reader user to know both the content in the tooltip, but also the content in the badge. For screen reader users, the only content announced to them is whatever is in the tooltip. For these cases, use the `aria-label` prop.

```jsx
// jsx-demo
function Example() {
	return (
		<Tooltip
			// What the tooltip renders
			label="Notifications"
			// What the screen reader announces
			aria-label="3 Notifications"
		>
			<button style={{ fontSize: 25 }}>🔔 3</button>
		</Tooltip>
	);
}
```

All props passed to `Tooltip` will go to the popover.

```jsx
// jsx-demo
function Example() {
	return (
		<Tooltip
			label="Fall in love all over again"
			style={{
				background: "hsla(0, 0%, 0%, 0.75)",
				color: "white",
				border: "none",
				borderRadius: "4px",
				padding: "0.5em 1em",
			}}
		>
			<button style={{ fontSize: 25 }}>❤️</button>
		</Tooltip>
	);
}
```

## Component API

### Tooltip

Renders … you guessed it: a tooltip!

#### Tooltip CSS Selectors

Please see the [styling guide](/styling).

```css
[data-reach-tooltip] {
}
```

### useTooltip, TooltipPopup

`Tooltip` is built on top of the `useTooltip` hook and `TooltipPopup`.

You may want to drop down to this lower level API if you need animation or you want to add the triangles to the tooltip. [Check out the demos below for examples.](#demos)

## Demos

### Triangle pointers and custom styles

```jsx
// jsx-demo
(() => {
	// Center the tooltip, but collisions will win
	const centered = (triggerRect, tooltipRect) => {
		const triggerCenter = triggerRect.left + triggerRect.width / 2;
		const left = triggerCenter - tooltipRect.width / 2;
		const maxLeft = window.innerWidth - tooltipRect.width - 2;
		return {
			left: Math.min(Math.max(2, left), maxLeft) + window.scrollX,
			top: triggerRect.bottom + 8 + window.scrollY,
		};
	};

	function TriangleTooltip({ children, label, "aria-label": ariaLabel }) {
		// get the props from useTooltip
		const [trigger, tooltip] = useTooltip();

		// destructure off what we need to position the triangle
		const { isVisible, triggerRect } = tooltip;

		return (
			<React.Fragment>
				{React.cloneElement(children, trigger)}

				{isVisible && (
					// The Triangle. We position it relative to the trigger, not the popup
					// so that collisions don't have a triangle pointing off to nowhere.
					// Using a Portal may seem a little extreme, but we can keep the
					// positioning logic simpler here instead of needing to consider
					// the popup's position relative to the trigger and collisions
					<Portal>
						<div
							style={{
								position: "absolute",
								left:
									triggerRect && triggerRect.left - 10 + triggerRect.width / 2,
								top: triggerRect && triggerRect.bottom + window.scrollY,
								width: 0,
								height: 0,
								borderLeft: "10px solid transparent",
								borderRight: "10px solid transparent",
								borderBottom: "10px solid black",
							}}
						/>
					</Portal>
				)}
				<TooltipPopup
					{...tooltip}
					label={label}
					aria-label={ariaLabel}
					style={{
						background: "black",
						color: "white",
						border: "none",
						borderRadius: "3px",
						padding: "0.5em 1em",
					}}
					position={centered}
				/>
			</React.Fragment>
		);
	}

	return (
		<div>
			<TriangleTooltip label="Notifications Really Long to Trigger Collision">
				<button style={{ fontSize: 25 }}>
					<span aria-hidden>🔔</span>
				</button>
			</TriangleTooltip>
			<TriangleTooltip label="Settings">
				<button style={{ fontSize: 25 }}>
					<span aria-hidden>⚙️</span>
				</button>
			</TriangleTooltip>

			<div style={{ float: "right" }}>
				<TriangleTooltip
					label="Notifications Really Long to Trigger Collision"
					aria-label="3 Notifications"
				>
					<button style={{ fontSize: 25 }}>
						<span>🔔</span>
						<span>3</span>
					</button>
				</TriangleTooltip>
			</div>
		</div>
	);
})();
```

### Animation

Here's some code you can copy/paste to make it happen along with [React Spring](http://react-spring.io):

```jsx
import * as React from "react";
import { useTooltip, TooltipPopup } from "@reach/tooltip";
import { useTransition, animated } from "@react-spring/web";

animated.TooltipPopup = animated(TooltipPopup);
animated.TooltipContent = animated(TooltipPopup);

function AnimatedTooltip({ children, ...rest }) {
	const [trigger, tooltip, isVisible] = useTooltip();

	const transitions = useTransition(isVisible, {
		from: { opacity: 0 },
		enter: { opacity: 1 },
		leave: { opacity: 0 },
		config: { mass: 1, tension: 500, friction: 40 },
	});

	return (
		<React.Fragment>
			{React.cloneElement(children, trigger)}
			{transitions(
				(styles, item) =>
					item && (
						<animated.TooltipContent {...tooltip} {...rest} style={styles} />
					)
			)}
		</React.Fragment>
	);
}

// Now you can replace `Tooltip` with `AnimatedTooltip`
function App() {
	return (
		<AnimatedTooltip label="Compose Message">
			<button>📝</button>
		</AnimatedTooltip>
	);
}
```
